package com.stox.services.core.repository;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.stox.shared.core.model.intf.Identifiable;
import com.stox.shared.core.util.Constant;
import com.stox.shared.core.util.PathUtil;

public abstract class AbstractCachingFileRepository<T extends Identifiable> implements Runnable {
	
	private long nextId = 1;
	private boolean dirty;
	private final String path;
	private final List<T> cache = new ArrayList<>();
	
	public AbstractCachingFileRepository(String name) {
		path = PathUtil.getRootPath()+"filebase"+File.separator+name+".csv";
		createIfNotExists();
		findAll();
	}
	
	private void createIfNotExists(){
		File file = new File(path);
		if(!file.exists()){
			file.getParentFile().mkdirs();
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void run() {
		if(dirty){
			commit();
		}
	}
	
	private void initCache(){
		if(cache.isEmpty()){
			BufferedReader reader = null;
			try {
				reader = new BufferedReader(new FileReader(path));
				String line = "";
				while(null != (line = reader.readLine())){
					T entity = deserialize(line);
					cache.add(entity);
					nextId = Math.max(nextId, entity.getId() + 1);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				if(null != reader){
					try {
						reader.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	public abstract T deserialize(String text);
	
	public abstract String serialize(T entity);
	
	public List<T> findAll(){
		initCache();
		return cache;
	}
	
	public boolean existsById(Long id){
		if(null == id) return false;
		initCache();
		for(T entity : cache){
			if(id.equals(entity.getId())){
				return true;
			}
		}
		return false;
	}
	
	public T findOne(Long id){
		if(null == id) return null;
		initCache();
		for(T entity : cache){
			if(id.equals(entity.getId())){
				return entity;
			}
		}
		return null;
	}
	
	public void delete(Long id){
		if(null == id) return;
		initCache();
		T deletableEntity = null;
		for(T entity : cache){
			if(id.equals(entity.getId())){
				deletableEntity = entity;
				break;
			}
		}
		if(null != deletableEntity){
			cache.remove(deletableEntity);
			dirty = true;
		}
	}
	
	public void clearSaveAndFlush(List<T> entities){
		cache.clear();
		cache.addAll(entities);
		commit();
	}
	
	public Long saveOrUpdate(T entity){
		if(null == entity){
			throw new RuntimeException("Can not persist null entity");
		}
		
		initCache();
		
		//Save
		if(null == entity.getId()){
			entity.setId(nextId++);
			cache.add(entity);
			dirty = true;
			return entity.getId();
		}else{
			//Update
			for(int index = 0; index < cache.size(); index++){
				T e = cache.get(index);
				if(entity.getId().equals(e.getId())){
					if(null == entity.getId()){
						entity.setId(e.getId());
					}
					cache.remove(index);
					cache.add(index, entity);
					dirty = true;
					return e.getId();
				}
			}
			throw new IllegalArgumentException("Entity to be updated(id="+entity.getId()+") was not found in file base");
		}
	}
	
	public void commit(){
		System.out.println("Commit : "+path);
		BufferedWriter writer = null;
		try{
			writer = new BufferedWriter(new FileWriter(path));
			for(T entity : cache){
				String line = serialize(entity)+Constant.lineSeparator;
				writer.write(line);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(null != writer){
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		dirty = false;
	}
	
	
}
